/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023.
 * Description: functions related to RTOS of irq-mbigen.
 * Author: wanglinhui <wanglinhui@huawei.com>
 * Create: 2023-09-24
 */

#if defined(CONFIG_RTOS_HAL_UART_CLEAR_MBIGEN_ACTIVE) || defined(CONFIG_RTOS_HAL_UGIC_IMIT)
static inline void get_mbigen_clear_reg(irq_hw_number_t hwirq,
					u32 *mask, u32 *addr);
#endif

#if defined(CONFIG_RTOS_HAL_LINEAR_MAP) || defined(CONFIG_RTOS_HAL_MBIGEN_LEVEL_SENSITIVE_BUGFIX)
struct mbigen_info mbigen_irq[HI1382_VIRQ_NUMS];
#endif

#ifdef CONFIG_RTOS_HAL_MBIGEN_KEEP_DEFAULT_SETTINGS
static int keep_default_type;
static int keep_default_ids;
#endif

#ifdef CONFIG_RTOS_HAL_MBIGEN_LEVEL_SENSITIVE_BUGFIX
#define MG_TIMEOUT_CFG 0x4
#endif

#ifdef CONFIG_RTOS_HAL_LPI_BYPASS_MBIGEN
#define MAX_NODE	16
static int lpi_bypass_mbigen_enable;
#endif

#if defined(CONFIG_RTOS_HAL_UGIC_IMIT) || defined(CONFIG_RTOS_HAL_LPI_BYPASS_MBIGEN)
#define get_mbigen_nid(hwirq)  ((hwirq + 64) / 128)
#endif

#ifdef CONFIG_RTOS_HAL_MBIGEN_LEVEL_SENSITIVE_BUGFIX
static inline void clear_active_if_needed(struct irq_data *data,
			u32 mask, u32 addr, void __iomem *base)
{
	struct irq_desc *desc = NULL;

	desc = container_of(data, struct irq_desc, irq_data);
	if (!desc || !desc->action)
		return;
	if (desc->action->flags & IRQF_NCLEAN_MBIGEN) {
		return;
	}
#ifdef CONFIG_RTOS_HAL_LPI_BYPASS_MBIGEN
	if (lpi_bypass_mbigen_enable) {
		/* if the SOC supports LPI reported bypass mbigen,
		 * then check whether it is through mbigen or not.
		 */
		if ((get_mbigen_nid(desc->irq_data.hwirq)) < MAX_NODE)
			writel_relaxed(mask, base + addr);
	} else {
		writel_relaxed(mask, base + addr);
	}
#else
	writel_relaxed(mask, base + addr);
#endif
}
#endif

#ifdef CONFIG_RTOS_HAL_UART_CLEAR_MBIGEN_ACTIVE
void clr_mbi_active(unsigned int irq)
{
	u32 mask, offset;
	void __iomem *base;
	struct irq_desc *desc = irq_to_desc(irq);

	if (desc == NULL)
		return;

	base = desc->irq_data.chip_data;
	get_mbigen_clear_reg(desc->irq_data.hwirq, &mask, &offset);

	/* we assume virq == irq while irq < 8192 */
	if (desc->irq_data.hwirq < MAXIMUM_IRQ_PIN_NUM)
		writel_relaxed(mask, base + offset);
}
#endif

#ifdef CONFIG_RTOS_HAL_MBIGEN_KEEP_DEFAULT_SETTINGS
static void parse_mbigen_type_dts(void)
{
	struct device_node *node = NULL;
	int err;

	node = of_find_compatible_node(NULL, NULL, "mbigen-rtos-configure");
	if (!node)
		return;

	err = of_property_read_u32(node, "keep-default-type", &keep_default_type);
	if (err)
		keep_default_type = 0;

	err = of_property_read_u32(node, "keep-default-ids", &keep_default_ids);
	if (err)
		keep_default_ids = 0;
#ifdef CONFIG_RTOS_HAL_LPI_BYPASS_MBIGEN
	err = of_property_read_u32(node, "lpi-bypass-mbigen-enable",
						&lpi_bypass_mbigen_enable);
	if (err)
		lpi_bypass_mbigen_enable = 0;
#endif
}
#endif

#ifdef CONFIG_RTOS_HAL_UGIC_IMIT
static const struct irq_domain_ops mbigen_domain_ops;

static int get_mbi_info(unsigned int irq, struct mbigen_info *mbigen_act_info)
{
	void __iomem *base = NULL;
	u32 mask, offset;
	struct irq_desc *desc = irq_to_desc(irq);

	if (mbigen_act_info == NULL)
		return -EINVAL;

	if (!desc) {
		mbigen_act_info->virq = 0;
		mbigen_act_info->mask = 0;
		mbigen_act_info->vaddr = NULL;
		return 0;
	}
	base = desc->irq_data.chip_data;
	if (desc->irq_data.domain->ops == &mbigen_domain_ops) {
#ifdef CONFIG_RTOS_HAL_LPI_BYPASS_MBIGEN
		if (lpi_bypass_mbigen_enable) {
			if (irq >= HI1382_LPI_MIN &&
			   (get_mbigen_nid(desc->irq_data.hwirq)) >= MAX_NODE) {
				mbigen_act_info->mask = 0;
				mbigen_act_info->vaddr = NULL;
				return 0;
			}
		}
#endif
		get_mbigen_clear_reg(desc->irq_data.hwirq, &mask, &offset);

		mbigen_act_info->mask = mask;
		mbigen_act_info->vaddr = base + offset;
	} else {
		mbigen_act_info->mask = 0;
		mbigen_act_info->vaddr = NULL;
	}
	return 0;
}

int clr_mbi_info(unsigned int irq, struct mbigen_info *mbigen_act_info)
{
	void __iomem *base = NULL;
	u32 mask, offset;
	struct irq_desc *desc = irq_to_desc(irq);

	if (!mbigen_act_info)
		return -EINVAL;

	if (!desc)
		return -EINVAL;

	if (desc->irq_data.domain &&
		(desc->irq_data.domain->ops == &mbigen_domain_ops)) {
#ifdef CONFIG_RTOS_HAL_LPI_BYPASS_MBIGEN
		if (lpi_bypass_mbigen_enable) {
			if ((get_mbigen_nid(desc->irq_data.hwirq)) >=
					MAX_NODE) {
				pr_notice("[%s:%d] irq%d not real MBIGEN.\n",
						__func__, __LINE__, irq);
				return -EINVAL;
			}
		}
#endif
		base = desc->irq_data.chip_data;
		get_mbigen_clear_reg(desc->irq_data.hwirq, &mask, &offset);
		writel_relaxed(mask, base + offset);
	} else {
		pr_notice("[%s:%d] irq%d not MBIGEN.\n",
				__func__, __LINE__, irq);
		return -EINVAL;
	}

	return 0;
}
EXPORT_SYMBOL_GPL(clr_mbi_info);

int mbigen_irq_init(void)
{
	int i;
	u64 irq;
	struct device_node *node = NULL;
	struct irq_domain *gic_domain = NULL;

	node = of_find_compatible_node(NULL, NULL, "arm,gic-v3");
	if (!node) {
		pr_err("[%s:%d] find dts node error!\n", __func__, __LINE__);
		return -EINVAL;
	}

	gic_domain = irq_find_host(node);
	for (i = 0; i < HI1382_SPI_NUMS; i++) {
		if (get_mbi_info(i, &(mbigen_irq[i]))) {
			pr_err("[%s:%d] get_mbi_info error!\n",
					__func__, __LINE__);
			return -EINVAL;
		}
	}

	for (i = 0; i < HI1382_VIRQ_NUMS - HI1382_SPI_NUMS; i++) {
		irq = irq_find_mapping(gic_domain, HI1382_LPI_MIN + i);
		if (irq) {
			if (get_mbi_info(irq,
				&(mbigen_irq[HI1382_SPI_NUMS+i]))) {
				pr_err("[%s:%d] get_mbi_info error!\n",
						__func__, __LINE__);
				return -EINVAL;
			}
		}
	}
	return 0;
}
EXPORT_SYMBOL_GPL(mbigen_irq_init);
#endif

bool hal_mbigen_set_type_ignore(struct irq_data *data)
{
#ifdef CONFIG_RTOS_HAL_MBIGEN_KEEP_DEFAULT_SETTINGS
	if (keep_default_type)
		return true;
#endif
#ifdef CONFIG_RTOS_HAL_LPI_BYPASS_MBIGEN
	if ((get_mbigen_nid(data->hwirq)) >= MAX_NODE)
		return true;
#endif
	return false;
}

bool hal_mbigen_write_ignore(struct irq_data *d)
{
#ifdef CONFIG_RTOS_HAL_MBIGEN_KEEP_DEFAULT_SETTINGS
	if (keep_default_ids)
		return true;
#endif
#ifdef CONFIG_RTOS_HAL_LPI_BYPASS_MBIGEN
	if ((get_mbigen_nid(d->hwirq)) >= MAX_NODE)
		return true;
#endif
	return false;
}
